/*
    socklibpp library
    Copyright (C) 2005  Daniel K. O. <danielosmari at users.sf.net>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/



#ifndef SOCKLISTENER_H
#define SOCKLISTENER_H

#include <list>
#include <utility>
#include <functional>

#include "sockbase.h"

namespace socklib {

/**     \file   socklistener.h
        \brief  Convenience class for testing many sockets for readability.
*/

/**
        \brief  This class is an efficient way for testing many sockets
                for readability/writeability.

        Just add socklib::base objects - with some optional data - to the listener;
        then use the readable() or for_each_read() methods to process incomming data,
        and writeable() or for_each_write() to process outgoing data.

        Here's an example that shows how to use a socklib::listener to read many
        connections; the \a getline() is used to show how the handling works
        in each iteraction; the \a USE_FOREACH macro can be undefined to show
        how to do the same thing without for_each_read().
        \include listener.cpp

*/

template<class T=int>
class listener {
        typedef std::list< std::pair<base, T> > _sock_list;
        _sock_list _sockets;

        struct Compare {
                bool operator()(const std::pair<base, T>& a,
                                const std::pair<base, T>& b)
                {       return a.fd == b.fd;    }
        };

public:

        /** Add a socket to be watched
                \param _sb      The socket to be watched
                \param _data    The data to be stored with this socket
                \note   It's not a good idea to add the same socket multiple
                        times.
        */
        void add(base _sb, const T& _data = T())
        {
                _sockets.push_back(std::make_pair(_sb, _data));
        }

        /** Remove a socket from the listener.
                \param _sb      The socket to be removed
        */
        bool remove(base _sb)
        {
                return _sockets.remove_if(std::bind2nd(Compare(), _sb));
        }
        
        
        /**     \brief  Return the number of sockets in the listener
        
                \note   Be careful, this function may take linear time. Use
                        empty() when you can.
                \see    empty
        */
        unsigned size() const throw() { return _sockets.size(); }


        /**     \brief  Return true if the listener is empty.
        
                Use empty() when you need to check if there's at last one socket
                in the listener; avoid using size() for this.

                \see    size
        */
        bool empty() const throw() { return _sockets.empty(); }


        /**     \brief  Test all sockets for readability, and return them in
                        a list.
                \param  readlist        a list where the pairs of socklib::base
                                        and \a T of each readable socket will be
                                        copied.
                \param usec     How much time to wait in each internal \a select()
                                call, in microseconds.
                \param sec      How much time to wait in each internal \a select()
                                call, in seconds.
                \note   The elements are simply appended on \a readlist; if
                        \a readlist is not empty before you call this function,
                        no element is removed.
                \note   \a select() may be called multiple times, depending on how
                        many sockets there are in the listener, and how big is
                        \a FD_SETSIZE; so the \a sec and \a usec parameteres may be
                        multiplied by ceil(size() / \a FD_SETSIZE).
                        e.g. if \a FD_SETSIZE=64, size()=260, and you call this
                        function with \a sec=1, you may have to wait for 5 seconds
                        until this function returns.
        */
        void readable(std::list<std::pair<base, T> >& readlist, int usec=0, int sec=0)
        {
                int i;
                typename _sock_list::const_iterator it = _sockets.begin(), first;
                fd_set readset;

                while (it!=_sockets.end()) {
                        FD_ZERO(&readset);
                        first = it;
                        // put all the sockets until FD_SETSIZE in the fd_set struct
                        for (i=0; i<FD_SETSIZE && it!=_sockets.end(); i++, ++it)
                                FD_SET(it->first.fd, &readset);

                        struct timeval tv = { sec, usec };

                        int r = ::select(FD_SETSIZE, &readset, 0, 0, &tv);

                        if (r==base::socket_error)
                                return;

                        if (r>0)
                                for (; first!=it; ++first)
                                        if (FD_ISSET(first->first.fd, &readset))
                                                readlist.push_back(*first);
                }
        }


        /**     \brief  Test all sockets for readability, and apply an operation
                        over them.

                \param  op      Must be a function or a functor, that returns
                                true if the socket should be removed from the
                                listener.
                \param usec     How much time to wait in each internal \a select()
                                call, in microseconds.
                \param sec      How much time to wait in each internal \a select()
                                call, in seconds.

                \note   You should never try to remove a socket from the
                        listener by hand; do it by returning true from \a op.
                        It's safe to add sockets to the listener.
                \note   \a select() may be called multiple times, depending on how
                        many sockets there are in the listener, and how big is
                        \a FD_SETSIZE; so the \a sec and \a usec parameteres may be
                        multiplied by ceil(size() / \a FD_SETSIZE).
                        e.g. if \a FD_SETSIZE=64, size()=260, and you call this
                        function with \a sec=1, you may have to wait for 5 seconds
                        until this function returns.
                \note   Some systems may give false-positives - i.e. select()
                        will lie about a socket being readable - when using
                        non-blocking mode. Be prepared for a socklib::base::try_again
                        error.
                \see socklib::base::recv(), socklib::base::block()
        */
        template<class Op>
        void for_each_read(Op op, int usec=0, int sec=0)
        {
                int _i;
                typename _sock_list::iterator
                        _it = _sockets.begin(),
                        _first;
                fd_set readset;

                while (_it!=_sockets.end()) {
                        FD_ZERO(&readset);
                        _first = _it;
                        // put all the sockets until FD_SETSIZE in the fd_set struct
                        for (_i=0; _i<FD_SETSIZE && _it!=_sockets.end(); _i++, ++_it)
                                FD_SET(_it->first.fd, &readset);

                        struct timeval tv = { sec, usec };

                        int _r = ::select(FD_SETSIZE, &readset, 0, 0, &tv);

                        if (_r==base::socket_error)
                                return;

                        if (_r>0)
                                for (; _first!=_it; ++_first)
                                        if (FD_ISSET(_first->first.fd, &readset))
                                                if (op(_first->first, _first->second))
                                                        _sockets.erase(_first--);
                }
        }


        /**     \brief  Test all sockets for writeability, and return them in
                        a list.
                \param  writelist       a list where the pairs of socklib::base
                                        and \a T of each writeable socket will be
                                        copied.
                \param usec     How much time to wait in each internal \a select()
                                call, in microseconds.
                \param sec      How much time to wait in each internal \a select()
                                call, in seconds.
                \note   The elements are simply appended on \a writelist; if
                        \a writelist is not empty before you call this function,
                        no element is removed.
                \note   \a select() may be called multiple times, depending on how
                        many sockets there are in the listener, and how big is
                        \a FD_SETSIZE; so the \a sec and \a usec parameteres may be
                        multiplied by ceil(size() / \a FD_SETSIZE).
                        e.g. if \a FD_SETSIZE=64, size()=260, and you call this
                        function with \a sec=1, you may have to wait for 5 seconds
                        until this function returns.
        */
        void writeable(std::list<std::pair<base, T> >& writelist, int usec=0, int sec=0)
        {
                int i;
                typename _sock_list::const_iterator it = _sockets.begin(), first;
                fd_set writeset;

                while (it!=_sockets.end()) {
                        FD_ZERO(&writeset);
                        first = it;
                        // put all the sockets until FD_SETSIZE in the fd_set struct
                        for (i=0; i<FD_SETSIZE && it!=_sockets.end(); i++, ++it)
                                FD_SET(it->first.fd, &writeset);

                        struct timeval tv = { sec, usec };
                        int r = ::select(FD_SETSIZE, 0, &writeset, 0, &tv);

                        if (r==base::socket_error)
                                return;

                        if (r>0)
                                for (; first!=it; ++first)
                                        if (FD_ISSET(first->first.fd, &writeset))
                                                writelist.push_back(*first);
                }
        }


        /**     \brief  Test all sockets for writeability, and apply an operation
                        over them.

                \param  op      Must be a function or functor, that receives
                                socklib::base and T as arguments, and returns
                                true if the socket should be removed from the
                                listener.
                \param usec     How much time to wait in each internal \a select()
                                call, in microseconds.
                \param sec      How much time to wait in each internal \a select()
                                call, in seconds.

                \note   You should never try to remove a socket from the
                        listener by hand; do it by returning true from \a op.
                        It's safe to add sockets to the listener.
                \note   \a select() may be called multiple times, depending on how
                        many sockets there are in the listener, and how big is
                        \a FD_SETSIZE; so the \a sec and \a usec parameteres may be
                        multiplied by ceil(size() / \a FD_SETSIZE).
                        e.g. if \a FD_SETSIZE=64, \a size()=260, and you call this
                        function with \a sec=1, you may have to wait for 5 seconds
                        until this function returns.
                \note   Many sockets are checked for writeability at once; if
                        you send too much data in \a op, you may fill the
                        buffers, and the other "writeable" sockets there were
                        checked together may block if you try to socklib::base::send()
                        over them. It's a good idea to make them non-blocking.
                \note   Some systems may give false-positives - i.e. select()
                        will lie about a socket being writeable - when using
                        non-blocking mode. Be prepared for a socklib::base::try_again
                        error.
                \see socklib::base::send(), socklib::base::block()
        */
        template<class Op>
        void for_each_write(Op op, int usec=0, int sec=0)
        {
                int _i;
                typename _sock_list::iterator
                        _it = _sockets.begin(),
                        _first;
                fd_set writeset;

                while (_it!=_sockets.end()) {
                        FD_ZERO(&writeset);
                        _first = _it;
                        // put all the sockets until FD_SETSIZE in the fd_set struct
                        for (_i=0; _i<FD_SETSIZE && _it!=_sockets.end(); _i++, ++_it)
                                FD_SET(_it->first.fd, &writeset);

                        struct timeval tv = { sec, usec };

                        int _r = ::select(FD_SETSIZE, 0, &writeset, 0, &tv);

                        if (_r==base::socket_error)
                                return;

                        if (_r>0)
                                for (; _first!=_it; ++_first)
                                        if (FD_ISSET(_first->first.fd, &writeset))
                                                if (op(_first->first, _first->second))
                                                        _sockets.erase(_first--);
                }
        }


};


}


#endif
